package com.mxpio.erp.workshop.policy.impl;

import com.mxpio.erp.common.enums.NoticeStatusEnums;
import com.mxpio.erp.common.enums.OrderEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.jpa.model.NoticeLine;
import com.mxpio.erp.common.jpa.model.Order;
import com.mxpio.erp.common.order.policy.AbstractBizOrderPolicy;
import com.mxpio.erp.common.order.policy.OrderContext;
import com.mxpio.erp.common.order.service.OrderLineService;
import com.mxpio.erp.common.technology.entity.Item;
import com.mxpio.erp.common.vo.NoticeClassVo;
import com.mxpio.erp.common.vo.OrderExcuteLineVo;
import com.mxpio.erp.common.workshop.entity.*;
import com.mxpioframework.common.util.BeanReflectionUtils;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.security.util.SecurityUtils;
import org.apache.commons.collections4.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 委外订单领/退/补料策略
 * @author mxpio
 *
 */
public class DefaultPickingOoPolicy extends AbstractBizOrderPolicy {
	
	private List<OrderExcuteLineVo> orderExcuteVos;
	
	// 退料 -1，补料 2， 领料 1
	private String type;

	private OrderLineService orderLineService;
	
	public DefaultPickingOoPolicy(List<OrderExcuteLineVo> orderExcuteVos, String type, OrderLineService orderLineService) {
		this.orderExcuteVos = orderExcuteVos;
		this.type = type;
		this.orderLineService = orderLineService;
	}
	
	@Override
	public void execute(OrderContext context) {
		// TODO 领/退/补策略
		
	}

	//重写执行方法
	@Override
	public void apply(OrderContext context) {
		if(beforeExecute(context)) {
			execute(context);
			OutsourceOrder order = (OutsourceOrder) context.getEntity();
			List<OrderExcuteLineVo> orderExcuteVos = context.getOrderExcuteVos();

			String[] accessTypes = context.getAccessType().split(",");
			for(String accessType : accessTypes){
				// 遍历执行行 生成通知单
				Map<String, OoPickingNotice> noticeMap = new HashMap<>();
				Map<String,List<OrderExcuteLineVo>> voMap = new HashMap<>();
				for(OrderExcuteLineVo orderExcuteVo : orderExcuteVos) {
					OoPickingNotice notice = noticeMap.get(orderExcuteVo.getWhCode());
					if(notice == null) {
						notice = (OoPickingNotice) BeanReflectionUtils.newInstance(OoPickingNotice.class);
						notice.setBizOrderNo(order.getBizNo());
						notice.setNoticeDate(new Date());
						notice.setWhCode(orderExcuteVo.getWhCode());
						notice.setAccessType(accessType);
						notice.setPickingType(context.getPickingType());
						notice.setNoticeStatus(NoticeStatusEnums.NoticeStatus.CREATE.getStatusCode());
						//处理冗余展示字段
						notice.setPnCode(order.getPnCode());
						notice.setPnName(order.getPnName());
						notice.setStartDate(order.getStartDate());
						notice.setEndDate(order.getEndDate());
						notice.setProductItemCode(order.getProductItemCode());
						notice.setProductItemName(order.getProductItemName());
						notice.setProductDrawingNo(order.getProductDrawingNo());

						noticeMap.put(orderExcuteVo.getWhCode(), notice);
						voMap.put(orderExcuteVo.getWhCode(), new ArrayList<OrderExcuteLineVo>());
					}
					voMap.get(orderExcuteVo.getWhCode()).add(orderExcuteVo);
				}
				// 生成通知单明细
				for(Map.Entry<String,OoPickingNotice> entry : noticeMap.entrySet()) {
					OoPickingNotice notice = entry.getValue();
					JpaUtil.save(notice);
					int lineNo = 0;
					for(OrderExcuteLineVo vo : voMap.get(entry.getKey())) {
						lineNo++;
						NoticeLine line = notice.createLine(vo);
						line.setLineNo(lineNo+"");
						Item item = JpaUtil.linq(Item.class).idEqual(vo.getItemCode()).findOne();
						line.setItemName(item.getItemName());
						line.setItemSpec(item.getItemSpec());
						line.setUnitCode(item.getUnitCode());
						line.setDrawingNo(item.getDrawingNo());
						line.setSubLotIndex(vo.getSubLotIndex());
						JpaUtil.save(line);
					}
				}
			}
			if(OrderStatusEnums.OrderStatus.PLAN.getStatusCode().equals(order.getOrderStatus())||
					OrderStatusEnums.OrderStatus.ORDERED.getStatusCode().equals(order.getOrderStatus())){
				order.setOrderStatus(OrderStatusEnums.OrderStatus.PROCESSING.getStatusCode());
				order.setExecuteTime(new Date());
				order.setExecutor(SecurityUtils.getLoginUsername());
				JpaUtil.update(order);
			}
			afterExecute(context);
		} else {
			if(CollectionUtils.isEmpty(context.getErrors())) {
				context.addError("前置校验不通过");
			}
		}
	}
	
	@Override
	public OrderContext config(OrderContext context) {
		context.setOrderExcuteVos(orderExcuteVos);
		context.setNoticeClass(OoPickingNotice.class);
		if(NoticeStatusEnums.NoticeType.OO_M_IN.getCode().equals(type)){//MO_M_IN("18", "委外退料"),
			context.setAccessType(OrderEnums.AccessType.IN.getCode());
			context.setPickingType(NoticeStatusEnums.NoticeType.OO_M_IN.getCode());
		}else if(NoticeStatusEnums.NoticeType.OO_M_OUT.getCode().equals(type)){//	MO_M_OUT("45", "委外领料"),
			context.setAccessType(OrderEnums.AccessType.OUT.getCode());
			context.setPickingType(NoticeStatusEnums.NoticeType.OO_M_OUT.getCode());
		}else{
			context.setAccessType(OrderEnums.AccessType.OUT.getCode());
			context.setPickingType(NoticeStatusEnums.NoticeType.OO_M_REOUT.getCode());//MO_M_REOUT("46", "委外补料"),
		}
		
		return context;
	}

	@Override
	public boolean beforeExecute(OrderContext context) {
		Order order = context.getEntity();
		if (order instanceof OutsourceOrder && context.getOrderExcuteVos().size()>0){
			List<OrderExcuteLineVo> orderExcuteVos = context.getOrderExcuteVos();
			List<String> orderlinesStr = orderExcuteVos.stream().map(OrderExcuteLineVo::getBizOrderlineNo).collect(Collectors.toList());
			List<OutsourceOrderItem> orderLines = JpaUtil.linq(OutsourceOrderItem.class).equal("bizNo", order.getBizNo()).in("lineNo", orderlinesStr).list();

			NoticeClassVo pickingNoticeClazz = new NoticeClassVo(OoPickingNotice.class, OoPickingNoticeLine.class);//领/补/退
			NoticeClassVo defectiveProductsNoticeClazz = new NoticeClassVo(OODefectiveProductsNotice.class, OODefectiveProductsNoticeLine.class);//不良品退料
			List<NoticeClassVo> noticeClassVos = new ArrayList<>();
			noticeClassVos.add(pickingNoticeClazz);
			noticeClassVos.add(defectiveProductsNoticeClazz);
			orderLineService.handleBizOrderLineQuantity(orderLines, noticeClassVos, OrderEnums.AccessType.OUT.getCode());
			Map<String, OutsourceOrderItem> orderLineMap = JpaUtil.index(orderLines,"lineNo");
			if(NoticeStatusEnums.NoticeType.OO_M_IN.getCode().equals(type)){//MO_M_IN("18", "委外退料"),
				for(OrderExcuteLineVo orderExcuteVo : orderExcuteVos) {
					// BigDecimal lineQuantity = orderExcuteVo.getLineQuantity();
					BigDecimal quantity = orderExcuteVo.getQuantity();
					String bizOrderlineNo = orderExcuteVo.getBizOrderlineNo();
					OutsourceOrderItem orderLine = orderLineMap.get(bizOrderlineNo);
					if (orderLine!=null){
						BigDecimal actualQuantity = orderLine.getActualQuantity();
						BigDecimal actualRejectQuantity = orderLine.getActualRejectQuantity();
						//BigDecimal planQuantity = orderLine.getPlanQuantity();
						BigDecimal planRejectQuantity = orderLine.getPlanRejectQuantity();
						//退料时，最大可退数量=已执行数量减去已执行退货数量减去计划退货数量，不考虑计划执行数量
						BigDecimal lastMax = actualQuantity.subtract(actualRejectQuantity).subtract(planRejectQuantity);
						if (quantity.compareTo(lastMax)>0){
							context.addError("提交数据中行号【"+bizOrderlineNo+"】执行数量大于最大可执行数量【"+lastMax+"】。");
							return false;
						}
					}
				}
			}else if(NoticeStatusEnums.NoticeType.OO_M_OUT.getCode().equals(type)){//	MO_M_OUT("45", "委外领料"),
				for(OrderExcuteLineVo orderExcuteVo : orderExcuteVos) {
					BigDecimal lineQuantity = orderExcuteVo.getLineQuantity();
					BigDecimal quantity = orderExcuteVo.getQuantity();
					String bizOrderlineNo = orderExcuteVo.getBizOrderlineNo();
					OutsourceOrderItem orderLine = orderLineMap.get(bizOrderlineNo);
					if (orderLine!=null){
						BigDecimal actualQuantity = orderLine.getActualQuantity();
						// BigDecimal actualRejectQuantity = orderLine.getActualRejectQuantity();
						BigDecimal planQuantity = orderLine.getPlanQuantity();
						//最大可领：执行行总数量减去实际已领，减去计划领料，不考虑实际退料数量，不考虑计划退货数量
						BigDecimal lastMax = lineQuantity.subtract(actualQuantity).subtract(planQuantity);//委外领料可领数量 只考虑正常领料/补料操作，不考虑退料，
						if (quantity.compareTo(lastMax)>0){
							context.addError("提交数据中行号【"+bizOrderlineNo+"】执行数量大于最大可执行数量【"+lastMax+"】。");
							return false;
						}
					}
				}
			}else{
				return true;//补料不验证
			}
		}
		return true;
	}

	@Override
	public void afterExecute(OrderContext context) {
		
	}

}
